#!/usr/bin/sh
#
# procsystime - print process system call time details.
#               Written using DTrace (Solaris 10 3/05).
#
# $Id: procsystime 4 2007-08-01 11:01:38Z brendan $
#
# USAGE:	procsystime [-acehoT] [ -p PID | -n name | command ]
#
#		-p PID          # examine this PID
#		-n name         # examine this process name
#		-a              # print all details
#		-c              # print syscall counts
#		-e              # print elapsed times
#		-o              # print CPU times
#		-T              # print totals
#         eg,
#		procsystime -p 1871     # examine PID 1871
#		procsystime -n tar      # examine processes called "tar"
#		procsystime -aTn bash   # print all details for bash shells
#		procsystime df -h       # run and examine "df -h"
#
# The elapsed times are interesting, to help identify syscalls that take
# some time to complete (during which the process may have slept). CPU time
# helps us identify syscalls that are consuming CPU cycles to run.
#
# FIELDS:
#		SYSCALL         System call name
#		TIME (ns)       Total time, nanoseconds
#		COUNT           Number of occurrences
#
# COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
#
# CDDL HEADER START
#
#  The contents of this file are subject to the terms of the
#  Common Development and Distribution License, Version 1.0 only
#  (the "License").  You may not use this file except in compliance
#  with the License.
#
#  You can obtain a copy of the license at Docs/cddl1.txt
#  or http://www.opensolaris.org/os/licensing.
#  See the License for the specific language governing permissions
#  and limitations under the License.
#
# CDDL HEADER END
#
# Author: Brendan Gregg  [Sydney, Australia]
#
# 27-Apr-2005   Brendan Gregg   Created this.
# 08-Jun-2005	   "      "	Added command option.
# 22-Sep-2005	   "      "	Allowed systemwide tracing.
# 22-Sep-2005	   "      "	Last update.
#


##############################
# --- Process Arguments ---
#

### Default variables
opt_filter=0; opt_pid=0; opt_name=0; pid=0; pname=".";
opt_elapsed=0; opt_cpu=0; opt_counts=0; opt_totals=0
opt_command=0; command="";

### Process options
while getopts acehn:op:T name
do
        case $name in
        p)      opt_filter=1; opt_pid=1; pid=$OPTARG ;;
        n)      opt_filter=1; opt_name=1; pname=$OPTARG ;;
	a)	opt_totals=1; opt_elapsed=1; opt_cpu=1; opt_counts=1 ;;
	e)	opt_elapsed=1 ;;
	c)	opt_counts=1 ;;
	o)	opt_cpu=1 ;;
	T)	opt_totals=1 ;;
        h|?)    cat <<-END >&2
		USAGE: procsystime [-aceho] [ -p PID | -n name | command ]
		                  -p PID          # examine this PID
		                  -n name         # examine this process name
		                  -a              # print all details
		                  -e              # print elapsed times
		                  -c              # print syscall counts
		                  -o              # print CPU times
		                  -T              # print totals
		  eg,
		       procsystime -p 1871     # examine PID 1871
		       procsystime -n tar      # examine processes called "tar"
		       procsystime -aTn bash   # print all details for bash
		       procsystime df -h       # run and examine "df -h"
		END
		exit 1
        esac
done
shift `expr $OPTIND - 1`

### Option logic
if [ $opt_pid -eq 0 -a $opt_name -eq 0 -a "$*" != "" ]; then
	opt_filter=1
	opt_command=1
	command="$*"
fi
if [ $opt_elapsed -eq 0 -a $opt_cpu -eq 0 -a $opt_counts -eq 0 ]; then
	opt_elapsed=1;
fi


#################################
# --- Main Program, DTrace ---
#
dtrace='
 #pragma D option quiet

 /*
  * Command line arguments
  */
 inline int OPT_elapsed  = '$opt_elapsed';
 inline int OPT_cpu      = '$opt_cpu';
 inline int OPT_counts   = '$opt_counts';
 inline int OPT_filter   = '$opt_filter';
 inline int OPT_pid      = '$opt_pid';
 inline int OPT_name     = '$opt_name';
 inline int OPT_totals   = '$opt_totals';
 inline int OPT_command  = '$opt_command';
 inline int PID          = '$pid';
 inline string NAME      = "'$pname'";
 inline string COMMAND   = "'$command'";

 dtrace:::BEGIN 
 {
	self->start = 0;
	self->vstart = 0;
 }
 dtrace:::BEGIN 
 /! OPT_command/
 {
	printf("Tracing... Hit Ctrl-C to end...\n");
 }

 /*
  * Set start timestamp and counts
  */
 syscall:::entry
 /(! OPT_filter) ||
  (OPT_pid && pid == PID) ||
  (OPT_name && execname == NAME) ||
  (OPT_command && pid == $target)/
 {
	self->ok = 1;
 }
 syscall:::entry
 /self->ok/
 {
	OPT_counts ? @Counts[probefunc] = count() : 1;
	(OPT_counts && OPT_totals) ? @Counts["TOTAL:"] = count() : 1;
	OPT_elapsed ? self->start = timestamp : 1;
	OPT_cpu ? self->vstart = vtimestamp : 1;
	self->ok = 0;
 }

 /*
  * Calculate time deltas
  */
 syscall:::return
 /self->start/
 {
	this->elapsed = timestamp - self->start;
	@Elapsed[probefunc] = sum(this->elapsed);
	OPT_totals ? @Elapsed["TOTAL:"] = sum(this->elapsed) : 1;
	self->start = 0;
 }
 syscall:::return
 /self->vstart/
 {
	this->cpu = vtimestamp - self->vstart;
	@CPU[probefunc] = sum(this->cpu);
	OPT_totals ? @CPU["TOTAL:"] = sum(this->cpu) : 1;
	self->vstart = 0;
 }

 /*
  * Elapsed time report
  */
 dtrace:::END 
 /OPT_elapsed/
 {
	printf("\nElapsed Times for ");
	OPT_pid ? printf("PID %d,\n\n",PID) : 1;
	OPT_name ? printf("processes %s,\n\n",NAME) : 1;
	OPT_command ? printf("command %s,\n\n",COMMAND) : 1;
	(! OPT_filter) ? printf("all processes,\n\n") : 1;
	printf("%16s %18s\n","SYSCALL","TIME (ns)");
	printa("%16s %@18d\n",@Elapsed);
 }

 /*
  * CPU time report
  */
 dtrace:::END 
 /OPT_cpu/
 {
	printf("\nCPU Times for ");
	OPT_pid ? printf("PID %d,\n\n",PID) : 1;
	OPT_name ? printf("processes %s,\n\n",NAME) : 1;
	OPT_command ? printf("command %s,\n\n",COMMAND) : 1;
	(! OPT_filter) ? printf("all processes,\n\n") : 1;
	printf("%16s %18s\n","SYSCALL","TIME (ns)");
	printa("%16s %@18d\n",@CPU);
 }

 /*
  * Syscall count report
  */
 dtrace:::END 
 /OPT_counts/
 {
	printf("\nSyscall Counts for ");
	OPT_pid ? printf("PID %d,\n\n",PID) : 1;
	OPT_name ? printf("processes %s,\n\n",NAME) : 1;
	OPT_command ? printf("command %s,\n\n",COMMAND) : 1;
	(! OPT_filter) ? printf("all processes,\n\n") : 1;
	printf("%16s %18s\n","SYSCALL","COUNT");
	OPT_counts ? printa("%16s %@18d\n",@Counts) : 1;
 }
'

### Run DTrace
if [ $opt_command -eq 1 ]; then
	/usr/sbin/dtrace -n "$dtrace" -x evaltime=exec -c "$command" >&2
else
	/usr/sbin/dtrace -n "$dtrace" >&2
fi

